﻿#region using

using System;
using System.IO;
using System.Text;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;

#endregion

namespace CipherLib.Cipher
{
    /// <summary>
    /// 
    /// </summary>
    public abstract class CipherBaseLib
    {
        #region consts

        //Certif d'exemple
        //makecert -r -pe -n "CN=test01" -b 01/01/2005 -e 01/01/2020 -sky exchange -ss my
        const X509FindType SUBJECTNAME_DEFAULT_FINDBY = X509FindType.FindBySubjectName;
        const StoreName STORENAME_DEFAULT_ENCRYPT = StoreName.My;
        const StoreLocation STORELOCATION_DEFAULT = StoreLocation.CurrentUser;

        #endregion

        #region members

        string _subjectName = string.Empty;
        X509FindType _findBy = SUBJECTNAME_DEFAULT_FINDBY;
        StoreName _encryptStoreName = STORENAME_DEFAULT_ENCRYPT;
        StoreLocation _storeLocation = STORELOCATION_DEFAULT;

        #endregion

        #region properties

        /// <summary>
        /// The storelocation <see cref="System.Security.Cryptography.StoreLocation"/> for the
        /// encryption. The decryption uses the information from the encrypted envelope (text).
        /// </summary>
        public StoreLocation StoreLocation
        {
            get { return _storeLocation; }
            set { _storeLocation = value; }
        }
        
        /// <summary>
        /// The storename <see cref="System.Security.Cryptography.StoreName"/> for the
        /// encryption. The decryption uses the information from the encrypted envelope (text).
        /// </summary>
        public StoreName EncryptStoreName
        {
            get { return _encryptStoreName; }
            set { _encryptStoreName = value; }
        }

        /// <summary>
        /// The critera for how the certicate is looked for. By default:
        /// <see cref="System.Security.Cryptography.X509FindType"/>.FindBySubjectName
        /// </summary>
        public X509FindType FindBy
        {
            get { return _findBy; }
            set { _findBy = value; }
        }
        
        /// <summary>
        /// The value for the criteria (by default the subject-name) for the certificat.
        /// </summary>
        public string SubjectName
        {
            get { return _subjectName; }
            set { _subjectName = value; }
        }

        #endregion

        #region constructors

        private CipherBaseLib(){}

        /// <summary>
        /// The instance of CryptoLib requires at least a CN-name (subjectName). The storelocation and storename are defaulted 
        /// as follow:
        /// const StoreName STORENAME_DEFAULT_ENCRYPT = System.Security.Cryptography.StoreName.My;
        /// const StoreLocation STORELOCATION_DEFAULT = System.Security.Cryptography.StoreLocation.CurrentUser;
        /// </summary>
        /// <param name="subjectName">The CN-name</param>
        protected CipherBaseLib(string subjectName) : this(subjectName, 
            STORENAME_DEFAULT_ENCRYPT, STORELOCATION_DEFAULT) { }

        /// <summary>
        /// The cryptoLib works with the subjectName, storeName and the storeLocation
        /// </summary>
        /// <param name="subjectName">The CN-name</param>
        /// <param name="defaultEncryptStoreName">for example System.Security.Cryptography.StoreName.My</param>
        /// <param name="defaultStoreLocation">for example System.Security.Cryptography.StoreLocation.CurrentUser</param>
        protected CipherBaseLib(string subjectName, 
            StoreName defaultEncryptStoreName, StoreLocation defaultStoreLocation)
        {
            if (String.IsNullOrEmpty(subjectName))
            {
                throw new ArgumentNullException(AppCommon.X509_SUBJECTNAME);
            }
            SubjectName = subjectName;
            EncryptStoreName = defaultEncryptStoreName;
            StoreLocation = defaultStoreLocation;
        }

        #endregion

        #region private / protected methods

        /// <summary>
        /// Gets the cetificate based on the storeName, storeLocation and the subjectName
        /// </summary>
        /// <param name="stroreName">StoreName.My</param>
        /// <param name="storeLocation">StoreLocation.LocalMachine</param>
        /// <returns>The certificate</returns>
        protected X509Certificate2 GetCertificate(StoreName storeName, 
            StoreLocation storeLocation)
        {
            X509Store store = new X509Store(storeName, storeLocation);
            X509Certificate2 certificate = null;
           
            try
            {
                store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
                certificate =
                    store.Certificates.Cast<X509Certificate2>().Where(
                    cert => cert.Subject.IndexOf(SubjectName) >= 0).FirstOrDefault();
                if (certificate == null)
                {
                    throw new ApplicationException(
                        String.Format(AppCommon.EX_X509_CERTIFICATE_NOT_FOUND,
                            SubjectName,
                            storeName,
                            storeLocation));
                }
            }
            finally
            {
                store.Close();
            }

            return certificate;
        }

        #endregion
    }
}